﻿using System;
using System.IO;
using System.Reflection;
using System.Collections.Generic;
using System.Collections;

using System.Text.RegularExpressions;

namespace AnB.Common
{
    public static class Utils
    {
        public static Dictionary<string, long> BinaryUnits = new Dictionary<string, long>()
        {
            { "B", 1 },
            { "K", 1024 },
            { "M", 1024 * 1024 },
            { "G", 1024 * 1024 * 1024 },
            // { "T", 1024 * 1024 * 1024 * 1024 },
        };
        public static Dictionary<string, long> DurationUnits = new Dictionary<string, long>()
        {
            { "MS", 1 },
            { "S" , 1000 },
            { "M" , 1000 * 60 },
            { "H" , 1000 * 60 * 60 },
            { "D" , 1000 * 60 * 60 * 24 },
            { "W" , 1000 * 60 * 60 * 24 * 7 },
        };
        public static long ParseBinary(string str)
        {
            return ParseQuantity(str, BinaryUnits);
        }
        public static long ParseDuration(string str)
        {
            return ParseQuantity(str, DurationUnits);
        }
        public static long ParseQuantity(string str, IDictionary<String, long> units)
        {
            var regex = new Regex(@"(?<digit>[0-9]+)(?<unit>[a-zA-Z]*)");
            long value = 0;
            int len = str.Length;
            int pos = 0;
            while (pos < len)
            {
                var m = regex.Match(str, pos);
                if (!m.Success)
                {
                    string error = string.Format("invalid format '{0}'@{1}", str.Substring(pos), pos);
                    throw new ArgumentException(error);
                }
                if (m.Index != pos)
                {
                    string error = string.Format("invalid format '{0}'@({1},{2})", str.Substring(pos, m.Index - pos), pos, m.Index);
                    throw new ArgumentException(error);
                }
                pos += m.Length;
                long d = long.Parse(m.Groups[1].Value);
                string u = m.Groups[2].Value;
                if (!string.IsNullOrEmpty(u))
                {
                    long u1;
                    if (!units.TryGetValue(u.ToUpper(), out u1) && !units.TryGetValue(u.ToLower(), out u1))
                    {
                        string error = string.Format("invalid unit '{0}'@({1},{2})", u, m.Groups[2].Index, m.Groups[2].Index);
                        throw new ArgumentException(error);
                    }
                    d *= u1;
                }
                value += d;
            }
            return value;
        }
        public static Type GetType(string name, IEnumerable<string> namespaces = null)
        {
            if (name == null || name.Length == 0)
            {
                return typeof(void);
            }
            switch (name)
            {
                case null:
                case "":
                    return typeof(void);
                case "int8":
                    return typeof(char);
                case "int16":
                    return typeof(short);
                case "int32":
                    return typeof(int);
                case "int64":
                    return typeof(long);
                case "string":
                    return typeof(string);
                case "bool":
                    return typeof(bool);
                case "bytes":
                    return typeof(byte[]);
            }

            var type = _GetType(name);
            if (type != null)
            {
                return type;
            }
            if (namespaces != null)
            {
                foreach (var namespace_ in namespaces)
                {
                    string name_ = string.Format("{0}.{1}", namespace_, name);
                    type = _GetType(name_);
                    if (type != null)
                    {
                        return type;
                    }
                }
            }
            return null;
        }
        private static Type _GetType(string name)
        {
            foreach (var assembly  in AppDomain.CurrentDomain.GetAssemblies())
            {
                Type type = assembly.GetType(name);
                if (type != null)
                {
                    return type;
                }
            }
            return null;
        }

        public static object[] Flat(params object[] ts)
        {
            var list = new List<object>();
            foreach (var t in ts)
            {
                var t1 = t as IEnumerable;
                if (t1 != null)
                {
                    var enumerator = t1.GetEnumerator();
                    while (enumerator.MoveNext())
                    {
                        list.Add(enumerator.Current);
                    }
                    continue;
                }
                list.Add(t);
            }
            return list.ToArray();
        }
    }
}
